/*
 * SingleDungeon.cpp
 *
 *  Created on: 2014. 3. 21.
 *      Author: zenoniaonline
 */

#include "SingleDungeon.h"

#include "../../User.h"
#include "../../macro.h"

#include "../../NLogic/Drop.h"
#include "../../NLogic/Space.h"
#include "../../NLogic/Monster.h"
#include "../../NLogic/Battle.h"
#include "../../NLogic/Party.h"
#include "../../NLogic/Status.h"

#include "../../NResource/Static.h"
#include "../../NResource/ValidResource.h"

namespace ProjectZ { namespace NProcess { namespace NCombat {

SingleDungeon::SingleDungeon() {
	// TODO Auto-generated constructor stub

}

SingleDungeon::~SingleDungeon() {
	// TODO Auto-generated destructor stub
}

void
SingleDungeon::Attack(User * pUser, ::Network::Packet * pPacket)
{
	NLogic::Space * pSpace = pUser->GetSpace();
	if (NULL == pSpace)
	{
		DEBUGLOG("SingleDungeon %s NULL == pSpace", __FUNCTION__);
		return;
	}

	if (true == pSpace->GetEndEungeon())
		return;

	/*
	 * // 던전에서 공격
	CN_ATTACK_DUNGEON	0x0515
	{
		u8	u8effect;		// 데미지 이펙트 출력 여부(0 없음 , 1 있음)
		u32	damage_value;	// Damage value(0 : 공격 실패, 0보다 큰경우 공격 성공)
		u32	defender_gid;	// DEFENDER GID
		u8	defender_ismonster;	// DEFENDER의 몬스터여부(0:주인공 1:몬스터)
		u16	degree;			방향 벡터(보고있는 방향 0 : RIGHT, 90 : DOWN..)
		u8	statetype;		// 상태이상여부 상위 4bit / 백어택여부 하위4bit
		u8	ismonster;		// 공격자의 몬스터여부 (0 : 주인공 1: 몬스터)
		u32	attacker_gid;	// 공격자 GID
	}

	SN_ATTACK_DUNGEON	0x0516
	{
		u8	u8effect;		// 데미지 이펙트 출력 여부(0 없음 , 1 있음)
		u32	damage_value;	// Damage value(0 : 공격 실패, 0보다 큰경우 공격 성공)
		u32	defender_gid;	// DEFENDER GID
		u8	defender_ismonster;	// DEFENDER의 몬스터여부(0:주인공 1:몬스터)
		u32	defender_hp;	// DEFENDER의 데미지 반영 후 HP
		u16	degree;			방향 벡터(보고있는 방향 0 : RIGHT, 90 : DOWN..)
		u8	ismonster;		// 공격자의 몬스터여부 (0 : 주인공 1: 몬스터)
		u32	attacker_gid;	// 공격자 GID
	}
 */

	Value clsValue;

	*pPacket >> clsValue.effect >> clsValue.damage_value >> clsValue.defender_gid;
	*pPacket >> clsValue.defender_ismonster >> clsValue.degree >> clsValue.statetype >> clsValue.ismonster >> clsValue.attacker_gid >> clsValue.attack_type;

	if (0 == clsValue.ismonster && 0 == clsValue.defender_ismonster) // ATTACK USER TO USER
	{
		DEBUGLOG("SingleDungeon::Attack ATTACK USER TO USER");
		return;
	}

	if (0 == clsValue.ismonster && 1 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (0 == clsValue.ismonster && 2 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (0 == clsValue.ismonster && 3 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (1 == clsValue.ismonster && 0 == clsValue.defender_ismonster) // attack user to monster
	{
		MonsterAttackUser(pUser, clsValue);
	}
	else if (1 == clsValue.ismonster && 1 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (1 == clsValue.ismonster && 2 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (1 == clsValue.ismonster && 3 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (2 == clsValue.ismonster && 0 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (2 == clsValue.ismonster && 1 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (2 == clsValue.ismonster && 2 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (2 == clsValue.ismonster && 3 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (3 == clsValue.ismonster && 0 == clsValue.defender_ismonster) // attack user to monster
	{
		MonsterAttackUser(pUser, clsValue);
	}
	else if (3 == clsValue.ismonster && 1 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (3 == clsValue.ismonster && 2 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (3 == clsValue.ismonster && 3 == clsValue.defender_ismonster) // attack user to monster
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (4 == clsValue.ismonster && 0 == clsValue.defender_ismonster)
	{
		MonsterAttackUser(pUser, clsValue);
	}
	else if (4 == clsValue.ismonster && 1 == clsValue.defender_ismonster)
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (4 == clsValue.ismonster && 2 == clsValue.defender_ismonster)
	{
		UserAttackMonster(pUser, clsValue);
	}
	else if (4 == clsValue.ismonster && 3 == clsValue.defender_ismonster)
	{
		UserAttackMonster(pUser, clsValue);
	}
	else
	{
	}
}

void
SingleDungeon::UserAttackMonster(User * pUser, Value & value)
{
	NResource::ValidResource::Flyweight * pFly = NResource::Static::Instance().GetValid()->GetFly(pUser->GetCharacterInfo().level);
	if (NULL == pFly)
	{
		DEBUGLOG("%s NULL == pFly", __FUNCTION__);
		return;
	}

	if (pFly->_DAMAGES < value.damage_value)
	{
		DEBUGLOG("CHEATUSER %s pFly->_DAMAGES < damage_value  pFly->_DAMAGES : %d damage : %d GID : %d", __FUNCTION__, pFly->_DAMAGES, value.damage_value, pUser->GetUserSeq());
		return;
	}

	NLogic::Battle * pBattle = pUser->GetBattle();
	if (NULL == pBattle)
	{
		DEBUGLOG("%s NULL == pBattle", __FUNCTION__);
		return;
	}

	if (pBattle->GetData().HP <= 0)
	{
		DEBUGLOG("%s pBattle->GetData().HP <= 0", __FUNCTION__);
		return;
	}

	int targetHP = 0;
	NLogic::Monster * pMonster = pUser->GetSpace()->GetMonster(value.defender_gid);
	if (NULL == pMonster)
		return;

	targetHP = pMonster->DecreaseHP(value.damage_value);
	if( targetHP == 0 )
	{
		if (pMonster->GetMonsterType() == NLogic::Monster::MonsterType::MONSTER)
		{
			NLogic::Drop clsDrop;
			clsDrop.ItemDrop(pUser, value.defender_gid);

			pUser->GetSpace()->RemoveMonster(value.defender_gid);
			pUser->GetBattle()->GetData().kill_monster_num++;

			NLogic::Space * pSpace = pUser->GetSpace();
			// 사냥 경험치 누적
			if (true == pSpace->IsPartyDungeon())
			{
				auto iter = pUser->GetParty()->GetAllUserTree().begin();
				for (; iter != pUser->GetParty()->GetAllUserTree().end(); ++iter)
				{
					int exp = getDungeonExp(iter->second, pMonster);
					iter->second->GetBattle()->GetData().hunting_exp += exp;
				}
			}
			else
			{
				int exp = getDungeonExp(pUser, pMonster);
				pUser->GetBattle()->GetData().hunting_exp += exp;
			}

			// 보스 사냥 완료 던전 종료....
			if (pMonster->GetData().tid == pUser->GetSpace()->GetData()._boss_tid)
			{
//				pUser->GetSpace()->GetData()._finishtick = NF::GetTickCount();
				pUser->GetSpace()->GetData()._finishtick = time(NULL);
				pUser->GetSpace()->MakeDungeonClearDetailInfoPacket();
				pUser->GetSpace()->SetEndDungeon(true);
			}
		}
	}

	pUser->GetBattle()->GetData().acc_damage += value.damage_value;

	if (pMonster->GetData().tid == pUser->GetSpace()->GetData()._boss_tid)
	{
		pUser->GetBattle()->GetData().acc_damage_boss_damage += value.damage_value;
		pUser->GetSpace()->GetData()._totalDamageToBoss += value.damage_value;
	}

//	DEBUGLOG("MONSTER ATTACKEDMONSTER DAMAGE : %d HP : %d MAX_HP : %d MONSTERGID : %d GID : %d",
//			value.damage_value,
//			targetHP,
//			pMonster->GetData().max_hp,
//			value.defender_gid,
//			pUser->GetUserSeq());

	{
		::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
		* pAck << value.effect;
		* pAck << value.damage_value;
		* pAck << value.defender_gid;
		* pAck << value.defender_ismonster;
		* pAck << (int)targetHP;
		* pAck << value.degree;
		* pAck << value.ismonster;
		* pAck << value.attacker_gid;
		* pAck << value.attack_type;
		pAck->MakePacket(CMD_SN_ATTACK_DUNGEON);
		pUser->GetSpace()->SendAll(pAck);
	}

	// 몬스터에 hp 정보 update 패킷 전송
	{
		::Network::Packet * pNoti = ::Network::GETPACKETPOOL()->SEND_NEW();
		* pNoti << value.defender_gid << targetHP;
		pNoti->MakePacket(CMD_SN_DUNGEON_UPDATE_MOB_HP);
		pUser->GetSpace()->SendAll(pNoti);
	}

	if (pMonster->GetData().tid == pUser->GetSpace()->GetData()._boss_tid && targetHP == 0)
	{
		DEBUGLOG("ATTACKEDMONSTER GID : %d DAMAGE : %d", pUser->GetUserSeq(), value.damage_value);
		::Network::Packet * pNoti = ::Network::GETPACKETPOOL()->SEND_NEW();
		*pNoti << (char)1;
		pNoti->MakePacket(CMD_SN_DUNGEON_MAP_CLEAR);
		pUser->GetSpace()->SendAll(pNoti);
	}
	// 더 이상 잡을 몬스터가 없는 경우 ...
	else if (pUser->GetSpace()->GetMonsterCount() == 0)
	{
		::Network::Packet * pNoti = ::Network::GETPACKETPOOL()->SEND_NEW();
		*pNoti << (char)1;
		pNoti->MakePacket(CMD_SN_DUNGEON_MAP_CLEAR);
		pUser->GetSpace()->SendAll(pNoti);
	}
	else
	{

	}
}

void
SingleDungeon::MonsterAttackUser(User * pUser, Value & value)
{
	NLogic::Space * pSpace = pUser->GetSpace();
	if (NULL == pSpace)
	{
		DEBUGLOG("%s NULL == pSpace", __FUNCTION__);
		return;
	}

	User * pTarget = pSpace->FindUser(value.defender_gid);
	if (NULL == pTarget)
	{
		DEBUGLOG("%s NULL == pTarget", __FUNCTION__);
		return;
	}

	if (true == pUser->GetSpace()->GetEndEungeon())
	{
		DEBUGLOG("%s true == pUser->GetSpace()->GetEndEungeon()", __FUNCTION__);
		return;
	}

	NLogic::Status * pStatus = pTarget->GetStatus();
	if (NULL == pStatus)
	{
		DEBUGLOG("%s %d NULL == pStatus", __FUNCTION__, __LINE__);
		return;
	}

	NLogic::Battle * pBattle = pTarget->GetBattle();
	if (NULL == pBattle)
	{
		DEBUGLOG("%s NULL == pBattle", __FUNCTION__);
		return;
	}

	if (pBattle->GetData().HP <= value.damage_value)
	{
		pBattle->GetData().HP = 0;
		pTarget->SetDeadTime( time(NULL) );
	}
	else
	{
		pBattle->GetData().HP -= value.damage_value;
	}

	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
	* pAck << value.effect;
	* pAck << value.damage_value;
	* pAck << value.defender_gid;
	* pAck << value.defender_ismonster;
	* pAck << static_cast<int32_t>(pBattle->GetData().HP);
	* pAck << value.degree;
	* pAck << value.ismonster;
	* pAck << value.attacker_gid;
	* pAck << value.attack_type;
	pAck->MakePacket(CMD_SN_ATTACK_DUNGEON);
	pUser->GetSpace()->SendAll(pAck);

	if (0 == pBattle->GetData().HP)
	{
		NLogic::Status::Data * pData = pStatus->GetStausData(NLogic::Status::StatusType::BATTLEPET_REBIRTH);
		if (NULL != pData && true == pData->_bRegist)
		{
			DEBUGLOG("BATTLEPET_REBIRTH CMD_SC_REBIRTH_DIRECT BATTLEPET_REBIRTH CALL GID : %d", pTarget->GetUserSeq());

			unsigned int hp = pTarget->GetBattle()->GetData().HP_MAX * pData->_value / 100;
			pTarget->GetBattle()->GetData().HP = hp;

			::Network::Packet * rebirth = ::Network::GETPACKETPOOL()->SEND_NEW();
			* rebirth << static_cast<uint8_t>(0);
			* rebirth << pTarget->GetUserSeq();
			* rebirth << hp;
			rebirth->MakePacket(CMD_SC_REBIRTH_DIRECT);
			pSpace->SendAll(rebirth);

			pData->Clear();
		}
	}
}

int
SingleDungeon::getDungeonExp(User * pUser, NLogic::Monster * monster)
{
	if (pUser == NULL)
	{
		DEBUGLOG("getDungeonExp pUser == NULL");
		return 0;
	}

	if (pUser->GetSpace() == NULL)
	{
		DEBUGLOG("getDungeonExp pUser->GetSpace() == NULL");
		return 0;
	}

	int user_level		= pUser->GetCharacterInfo().level;
	int monster_level	= monster->GetData().level;
	int islegend		= monster->GetData().islegend;
	int monster_tid		= monster->GetData().tid;
	int difficulty		= pUser->GetSpace()->GetData()._difficulty;
	bool isboss 		= (monster_tid == pUser->GetSpace()->GetData()._boss_tid) ? true : false;

	NResource::BalanceData * pBalance = NResource::Static::InstancePtr()->GetBalance()->GetBalancePtr(pUser);
	if (NULL == pBalance)
	{
		DEBUGLOG("%s : pBalance is NULL!", __FUNCTION__);
		return 0;
	}

	// 몬스터별 가중치 상수
	int addMonster = pBalance->GetValue(EM_NORMAL_ADD_EXP);
	if (isboss)
	{
		addMonster = pBalance->GetValue(EM_BOSS_ADD_EXP);
	}
	else if (islegend)
	{
		addMonster = pBalance->GetValue(EM_LEGEND_ADD_EXP);
	}

	// 난이도별 가중치 상수
	int addMode = pBalance->GetValue(EM_NORMAL_MOB_EXP_CONST);
	if (difficulty == 1)
	{
		addMode = pBalance->GetValue(EM_HARD_MOB_EXP_CONST);
	}
	else if (difficulty == 2)
	{
		addMode = pBalance->GetValue(EM_HELL_MOB_EXP_CONST);
	}

	// 3 * 레벨^2 + 상수(10) * 레벨
	int exp = ( pow(monster_level, 2) * 3 ) + (DUNGEON_EXP_CONSTANT * monster_level);

	// 레벨차 별 가중치
	int level_gap = monster_level - user_level;

	if (10 <= level_gap)
	{
		exp = (exp * pBalance->GetValue(EM_EXP_CONST_OVER_LEVEL_10)) / 100;
	}
	else if (8 <= level_gap)
	{
		exp = (exp * pBalance->GetValue(EM_EXP_CONST_OVER_LEVEL_8)) / 100;
	}
	else if (6 <= level_gap)
	{
		exp = (exp * pBalance->GetValue(EM_EXP_CONST_OVER_LEVEL_6)) / 100;
	}
	else if (4 <= level_gap)
	{
		exp = (exp * pBalance->GetValue(EM_EXP_CONST_OVER_LEVEL_4)) / 100;
	}
	else if (2 <= level_gap)
	{
		exp = (exp * pBalance->GetValue(EM_EXP_CONST_OVER_LEVEL_2)) / 100;
	}
	else if (0 <= level_gap)
	{
		exp = (exp * pBalance->GetValue(EM_EXP_CONST_OVER_LEVEL_0)) / 100;
	}
	else if (-2 <= level_gap)
	{
		exp = (exp * pBalance->GetValue(EM_EXP_CONST_UNDER_LEVEL_2)) / 100;
	}
	else if (-4 <= level_gap)
	{
		exp = (exp * pBalance->GetValue(EM_EXP_CONST_UNDER_LEVEL_4)) / 100;
	}
	else if (-6 <= level_gap)
	{
		exp = (exp * pBalance->GetValue(EM_EXP_CONST_UNDER_LEVEL_6)) / 100;
	}
	else if (-8 <= level_gap)
	{
		exp = (exp * pBalance->GetValue(EM_EXP_CONST_UNDER_LEVEL_8)) / 100;
	}
	else
	{
		exp = (exp * pBalance->GetValue(EM_EXP_CONST_UNDER_LEVEL_10_OVER)) / 100;
	}

	// 최종 경험치
	// 기본 경험치 * 몬스터 타입별 경험치 상수/100 * 모드별 사냥 경험치/100
	int get_exp = exp * (addMonster / 100.0) * (addMode / 100.0);

	return get_exp;
}

} /*NCombat*/ } /*NProcess*/ } /*ProjectZ*/

